GESIS Workshop: Introduction to Geospatial Techniques for Social Scientists in R
Stefan Jünger & Dennis Abel
2025-04-10
Now
Day
Time
Title
April 09
10:00-11:30
Introduction
April 09
11:30-11:45
Coffee Break
April 09
11:45-13:00
Data Formats
April 09
13:00-14:00
Lunch Break
April 09
14:00-15:30
Mapping I
April 09
15:30-15:45
Coffee Break
April 09
15:45-17:00
Spatial Wrangling
April 10
09:00-10:30
Mapping II
April 10
10:30-10:45
Coffee Break
April 10
10:45-12:00
Applied Spatial Linking
April 10
12:00-13:00
Lunch Break
April 10
13:00-14:30
Spatial Autocorrelation
April 10
14:30-14:45
Coffee Break
April 10
14:45-16:00
Spatial Econometrics & Outlook
What is ggplot2?
ggplot2 is well-known for creating plots. Thanks to our sf and terra, we can exploit all amazing ggplot2 functions.
In general, on ggplot2:
Well-suited for multi-dimensional data
Expects data (frames) as input
Components of the plot are added as layers
plot_call + layer_1 + layer_2 + ... + layer_n
From tmap to ggplot2
Reminder: We played around with tmap yesterday, and the results were already pretty nice. ggplot2 allows us to customize our maps even more, draw on previous knowledge of the package, increase the possibilities to combine maps and plots, and more.
The good thing is that the inner logic of tmap and ggplot2 is the same and is based on the grammar of graphics.
If you are new to ggplot2, you might want to check out:
# load district shapefilegerman_districts <- sf::read_sf("./data/VG250_KRS.shp")# load district attributesattributes_districts <- readr::read_csv2("./data/attributes_districts.csv") |> dplyr::mutate(ecar_share =as.numeric(ecar_share))# join datagerman_districts_enhanced <- german_districts |> dplyr::left_join(attributes_districts, by ="AGS")# load states shapefilegerman_states <- sf::read_sf("./data/VG250_LAN.shp")
Here’s a first basic map
# a simple first map ggplot() +geom_sf(data = german_districts_enhanced)
Making a plan
This map will be our canvas for the ongoing session. There are hundreds of options to change this map. We will cover at least some essential building blocks:
THE MAP: adding attributes, choosing from colors/palettes, adding layers
THE LEGEND: position, sizes, display
THE ENVIRONMENT: choosing from themes and build your own
THE META-INFORMATION: titles and sources
THE EXTRAS: scales and compass
If you are working on your maps, the ggplot2 cheatsheets will help you with an overview of scales, themes, labels, facets, and more.
The map layer: a basis
# easy fill with colorggplot() +geom_sf(data = german_districts_enhanced, fill ="purple", color ="blue" )
Add the aesthetics
We’ll concentrate on mapping the e-car share at the district level.
# map aetheticsggplot() +geom_sf(data = german_districts_enhanced, # add the attribute we want to mapaes(fill = ecar_share) ) +# choose a continuous palette scale_fill_continuous()
The map layer
Are you having trouble choosing the right color? Some excellent tutorials exist, f.e, by Michael Toth.
# change color paletteggplot() +geom_sf(data = german_districts_enhanced, aes(fill = ecar_share) ) +# readable with color vision deficienciesscale_fill_viridis_c(option ="plasma")
# the shapefile includes polygons of oceans and lakes# easy fix on the fly when you know your datagerman_states <- german_states |> dplyr::filter(GF ==4)# add layer with German statesggplot() +geom_sf(data = german_districts_enhanced, aes(fill = ecar_share), color =NA ) +scale_fill_viridis_c(option ="plasma", direction =-1 ) +# add another layergeom_sf(data = german_states, # filling transparentfill ="transparent",# color of borderscolor ="black", # size of borderssize =1 )
Dealing with the Legend
You can handle everything concerning the legend (labels, titles, width…) within the scale argument. The only thing you cannot change here is the position in relation to the map.
ggplot() +geom_sf(data = german_districts_enhanced, aes(fill = ecar_share), color =NA ) +scale_fill_viridis_c(option ="plasma",direction =-1,# add a legend titlename ="E-Car Share",# adjust legendguide =guide_legend(# turn it horizontaldirection="horizontal",# put the labels# under the legend barlabel.position ="bottom" ) ) +geom_sf(data = german_states, fill ="transparent", color ="black" ) # check the help file for more options ?guide_legend
Save and reuse
Maps produced with ggplot2 are standard objects like any other object in R (they are lists). We can assign them to reuse, plot later, and add map layers.
Furthermore, you can save them just as any ggplot2 graph. The ggsave() function automatically detects the file format. You can also define the height, width, and dpi, which is particularly useful to produce high-class graphics for publications.
Save and reuse
# assign to objectecar_map <-ggplot() +geom_sf(data = german_districts_enhanced, aes(fill = ecar_share), color =NA ) +scale_fill_viridis_c(option ="plasma",direction =-1,name ="E-Car Share",guide =guide_legend(direction="horizontal",label.position ="bottom" ) ) +geom_sf(data = german_states, fill ="transparent", color ="black" ) # save as png-file# ggsave("ecar_map.png", ecar_map, dpi = 300)
Get rid of everything?!
The theme controls all ‘non-data’ displays. If you want to get rid of the default ggplot2 theme, you can do so. Instead of removing everything, you should try out the built-in themes.
# use the object ecar_map as base layerecar_map +# empty your themetheme_void() # ... or add another# theme_bw()# theme_gray()# theme_light()# check all themes here# ?theme
Build your own theme
# building a themeecar_map +theme_void() +# bold text elementstheme(title =element_text(face ="bold"), # move legend to bottom of maplegend.position ="bottom", # change background colorpanel.background =element_rect(fill ="lightgrey") )
Adding labs
There is one necessary step to do. You should always make sure to include and cite your data sources. Especially in graphs and maps, you can use a short version to include them directly in the description.
Our code has already grown. Without going into too much detail, the following slides showcase some more changes you can make to your maps
A map is never finished until you decide not to work on it anymore.
Creating a city layer for city labels
# create a german city layer by choosing the five districts# with the highest pop densitydistricts_centroids <- german_districts_enhanced |># calculate pop_dens dplyr::mutate(pop_dens = population / sf::st_area(german_districts_enhanced) ) |># filter top 5 observation with highs pop_dens dplyr::top_n(5, pop_dens) |># take the centroid of each polygon and turn to# polygon file into a vector sf::st_centroid() city_coordinates <- districts_centroids |> sf::st_coordinates() |>as.data.frame() german_cities <- dplyr::bind_cols(districts_centroids, city_coordinates) |># add some city names as labels dplyr::bind_cols(data.frame(names =c("City 1", "City 2", "City 3", "City 4", "City 5") ) )german_cities |> dplyr::select(pop_dens, X, Y)
Simple feature collection with 5 features and 3 fields
Geometry type: POINT
Dimension: XY
Bounding box: xmin: 432953.5 ymin: 5283785 xmax: 670634.3 ymax: 5998091
Projected CRS: ETRS89 / UTM zone 32N
# A tibble: 5 × 4
pop_dens X Y geometry
[1/m^2] <dbl> <dbl> <POINT [m]>
1 2.03 501787. 5964329. (501786.6 5964329)
2 0.786 432954. 5947975. (432953.5 5947975)
3 1.25 469708. 5935810. (469708.3 5935810)
4 0.933 504505. 5283785. (504505.2 5283785)
5 2.07 670634. 5998091. (670634.3 5998091)
Add City Labels
Using geom_label to get a text box with a character string associated with an X- and Y-coordinate.
ecar_map +# add the labelgeom_label(data = german_cities, # don't need sf object but columns # with x- and y-coordinateaes(x = X, y = Y, # column holding the character# vector with stringslabel = names ),# size of labelssize =3,# transparencyalpha = .8 )
ggplot2 and raster data
You can also use ggplot2 to create maps with raster data. There are several ways to do so. The easiest way is using the tidyterra package.
In some specific circumstances, we might realize that ggplot2 is super powerful but not originally designed to build maps. Typical features of maps are not in the package, like a compass or scale bars.
This is where other packages might need to be installed. The good thing: Elements of the package ggspatial can be included as ggplot2 layer. Check out Github.
The extras
ggspatial allows you to add, f.e. a scale bar and a north arrow.
# add scalebar and north arrowecar_map + ggspatial::annotation_scale(location ="br" ) + ggspatial::annotation_north_arrow(location ="tr", style = ggspatial::north_arrow_minimal() )